package com.duobaoyu.dby.supply.biz.service.refund.event;

import com.alibaba.fastjson.JSON;
import com.duobaoyu.dby.channel.adapter.enums.biz.refund.AfterSaleStatusEnum;
import com.duobaoyu.dby.supply.biz.converter.AfterSaleServiceConverter;
import com.duobaoyu.dby.supply.biz.enums.AfterSaleStateLogEnum;
import com.duobaoyu.dby.supply.biz.enums.OperationLogTypeEnum;
import com.duobaoyu.dby.supply.biz.producer.SupplyChainMQProducer;
import com.duobaoyu.dby.supply.biz.service.config.OperationLogService;
import com.duobaoyu.dby.supply.common.biz.bo.refund.AfterSaleItemOutBO;
import com.duobaoyu.dby.supply.common.biz.bo.refund.AfterSaleOutBO;
import com.duobaoyu.dby.supply.common.biz.bo.refund.AfterSaleStateContext;
import com.duobaoyu.dby.supply.common.biz.dto.config.OperationLogSaveDTO;
import com.duobaoyu.dby.supply.common.mq.remote.constants.BizMQConstants;
import com.duobaoyu.dby.supply.common.mq.remote.message.AfterSaleStateChangeMessage;
import com.duobaoyu.dby.supply.core.dinger.annotation.DingerTalk;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.builder.Diff;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.duobaoyu.dby.supply.biz.producer.constant.SupplyChainMQConstants.SupplyChainTopic.AFTER_SALE_TOPIC;
import static com.duobaoyu.dby.supply.biz.producer.constant.SupplyChainMQConstants.SupplyChainTopic.AfterSaleTag.AFTER_SALES_STATUS_CHANGE;

/**
 * Created by shuming on 2023/10/17 16:44
 */
@Component
@AllArgsConstructor
@Slf4j
public class AfterSaleStateChangeEventListener {

    private final AfterSaleServiceConverter afterSaleServiceConverter;
    private final OperationLogService operationLogService;
    private final SupplyChainMQProducer supplyChainMQProducer;

    @TransactionalEventListener
    @DingerTalk(title = "售后状态监听发生异常")
    public void handleAfterSaleOrderEvent(AfterSaleStateChangeEvent event) {
        // 操作日志记录
        try {
            operaLog(event.getContext());
        } catch (Exception e) {
            log.warn("record log throw exceptions,param={}", JSON.toJSONString(event), e);
        }
        supplyChainMQProducer.afterSaleApplicationMsgSend(AFTER_SALE_TOPIC, AFTER_SALES_STATUS_CHANGE, event);
        if (!event.getOrderAutoCancel()) {
            AfterSaleStateChangeMessage message = new AfterSaleStateChangeMessage();
            message.setAfterSaleSn(event.getAfterSaleSn());
            message.setAfterSaleType(event.getAfterSaleType());
            message.setAfterSaleStatus(event.getAfterSaleStatus());
            supplyChainMQProducer.afterSaleExternalMsgSend(BizMQConstants.AfterSaleTopic.ORDER_CENTER_AFTER_SALE_TOPIC,
                    BizMQConstants.AfterSaleTopic.Tag.STATUS_CHANGE_TAG, message);
            // 发送退款消息
            if (Optional.of(event).map(AfterSaleStateChangeEvent::getRefundSuccess).orElse(Boolean.FALSE)) {
                supplyChainMQProducer.afterSaleExternalMsgSend(BizMQConstants.AfterSaleTopic.ORDER_CENTER_AFTER_SALE_TOPIC,
                        BizMQConstants.AfterSaleTopic.Tag.REFUND_TAG, message);
            }
        }
    }

    /**
     * 操作日志
     *
     * @param context
     */
    @SneakyThrows
    public void operaLog(AfterSaleStateContext context) {
        if (Objects.isNull(context) || Objects.isNull(context.getAfterSale())) {
            return;
        }
        AfterSaleOutBO oldAfterSaleOut = context.getAfterSale();
        AfterSaleOutBO newAfterSaleOut = this.buildChangedAfterSale(context);
        // compare
        List<Diff<?>> diffList = Lists.newArrayList();
        diffList.addAll(oldAfterSaleOut.diff(newAfterSaleOut).getDiffs());
        newAfterSaleOut.getItems().forEach(e -> {
            oldAfterSaleOut.getItems().forEach(item -> {
                if (Objects.equals(e.getId(), item.getId())) {
                    diffList.addAll(item.diff(e).getDiffs());
                }
            });
        });
        OperationLogSaveDTO operationLogSave = OperationLogSaveDTO.builder()
                .diffList(diffList)
                .scene(String.format(Objects.requireNonNull(AfterSaleStateLogEnum
                        .getSaleLogDescBySaleStatusEnum(AfterSaleStatusEnum
                                .getAfterSaleStatusEnum(context.getCurrentAfterSaleStatus()))), context.getOperateSource()))
                .logType(OperationLogTypeEnum.AFTER_SALE.getValue())
                .bizKey(context.getAfterSale().getAfterSaleSn())
                .source(context.getOperateSource())
                .build();
        operationLogService.save(operationLogSave);
    }

    private AfterSaleOutBO buildChangedAfterSale(AfterSaleStateContext context) {
        AfterSaleOutBO afterSaleOut = afterSaleServiceConverter.assembleAfterSaleOut(context.getAfterSale());
        AfterSaleStateContext.ChangeContentOperate contentOperate = context.getChangeContentOperate();
        if (Objects.nonNull(context.getCurrentAfterSaleStatus())) {
            afterSaleOut.setStatus(context.getCurrentAfterSaleStatus());
        }
        if (contentOperate != null) {
            afterSaleOut.setChannelAfterSaleSn(contentOperate.getChannelAfterSaleSn());
            if (Objects.nonNull(contentOperate.getFailReason())) {
                afterSaleOut.setFailReason(contentOperate.getFailReason());
            }
            if (Objects.nonNull(contentOperate.getCancelRemark())) {
                afterSaleOut.setCancelReason(contentOperate.getCancelRemark());
            }
            if (Objects.nonNull(contentOperate.getCustomerReturnAddress())) {
                afterSaleOut.setCustomerReturnAddress(contentOperate.getCustomerReturnAddress());
            }
            if (Objects.nonNull(contentOperate.getChannelActualRefundAmount())) {
                afterSaleOut.setChannelActualRefundAmount(contentOperate.getChannelActualRefundAmount());
            }
            if (Objects.nonNull(contentOperate.getFreightApplyRefundAmount())) {
                afterSaleOut.setFreightApplyRefundAmount(contentOperate.getFreightApplyRefundAmount());
            }
            if (Objects.nonNull(contentOperate.getChannelFreightActualRefundAmount())) {
                afterSaleOut.setChannelFreightActualRefundAmount(contentOperate.getChannelFreightActualRefundAmount());
            }

            if (CollectionUtils.isNotEmpty(contentOperate.getAfterSaleItemInfoList())) {
                Map<Long, AfterSaleStateContext.AfterSaleUpdateItemInfo> updateItemInfoMap = contentOperate.getAfterSaleItemInfoList()
                        .stream().collect(Collectors.toMap(AfterSaleStateContext.AfterSaleUpdateItemInfo::getId, Function.identity()));
                List<AfterSaleItemOutBO> items = afterSaleOut.getItems();

                List<AfterSaleItemOutBO> newItems = Lists.newArrayList();
                for (AfterSaleItemOutBO item : items) {
                    AfterSaleItemOutBO afterSaleItemOut = afterSaleServiceConverter.assembleAfterSaleItemOut(item);
                    newItems.add(afterSaleItemOut);
                    AfterSaleStateContext.AfterSaleUpdateItemInfo afterSaleUpdateItemInfo = updateItemInfoMap.get(item.getId());
                    if (Objects.isNull(afterSaleUpdateItemInfo)) {
                        continue;
                    }
                    if (Objects.nonNull(afterSaleUpdateItemInfo.getGoodsStatus())) {
                        afterSaleItemOut.setGoodsStatus(afterSaleUpdateItemInfo.getGoodsStatus());
                    }
                    if (Objects.nonNull(afterSaleUpdateItemInfo.getReasonCode())) {
                        afterSaleItemOut.setReasonCode(afterSaleUpdateItemInfo.getReasonCode());
                    }
                    if (Objects.nonNull(afterSaleUpdateItemInfo.getReasonName())) {
                        afterSaleItemOut.setReasonName(afterSaleUpdateItemInfo.getReasonName());
                    }
                    if (Objects.nonNull(afterSaleUpdateItemInfo.getChannelApplyAfterSaleAmount())) {
                        afterSaleItemOut.setApplyRefundAmount(afterSaleUpdateItemInfo.getChannelApplyAfterSaleAmount());
                    }
                    if (Objects.nonNull(afterSaleUpdateItemInfo.getFreightApplyRefundAmount())) {
                        afterSaleItemOut.setFreightApplyRefundAmount(afterSaleUpdateItemInfo.getFreightApplyRefundAmount());
                    }
                    if (Objects.nonNull(afterSaleUpdateItemInfo.getChannelActualRefundAmount())) {
                        afterSaleItemOut.setChannelActualRefundAmount(afterSaleUpdateItemInfo.getChannelActualRefundAmount());
                    }
                    if (Objects.nonNull(afterSaleUpdateItemInfo.getChannelFreightActualRefundAmount())) {
                        afterSaleItemOut.setChannelFreightActualRefundAmount(afterSaleUpdateItemInfo.getChannelFreightActualRefundAmount());
                    }
                }
                afterSaleOut.setItems(newItems);
            }
        }
        return afterSaleOut;
    }
}
